package org.haredot.utils;


import org.haredot.annotation.Component;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.*;
import java.util.stream.Collectors;

public class ClassUtils {
    /**
     * 用来存储 CLASSPATH 的父级目录
     */
    public static final File CLASS_PATH;

    public static final List<Class<? extends Annotation>> META_ANNOTATIONS =
            List.of(Target.class, Retention.class, Documented.class, Inherited.class, Repeatable.class);

    static {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String classpath = classLoader.getResource("").getPath();

        // 4. 获取 类路径 文件
        try {
            CLASS_PATH = new File(URLDecoder.decode(classpath, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 用来获取 classpath下的类文件
     * @return
     */
    public static List<File> getClassFile() {
        // 读取 path 目录 下 所有的 .class 文件
        return readClassFile(CLASS_PATH);
    }

    public static List<Class<?>> getClasses() throws Exception {
        /**
         *  一个类的类对象 只有一个、发生在 类加载
         *  类对象 的获取方式 有三种
         *  1.  类.class  ;
         *  2.  对象.getClass() ;
         *  3.  Class.forName("类的全名") ;
         */
        List<String> classPath = getClassPath();
        // 将 classpath 下所有的类 全部进行加载
        return classPath.stream().map(clz -> {
            try {
                return Class.forName(clz);
            } catch (Exception e) {
                return null;
            }
        }).collect(Collectors.toList());
    }

    /**
     * 获取 classpath 下的所有 类信息
     *
     * @return
     */
    public static List<String> getClassPath() {
        List<File> classFile = getClassFile();
        // 遍历 classFile ,并对 File 数据进行 映射 、返回 String
        return classFile.stream().map(file -> {
            try {
                // 获取 路径
                String canonicalPath = file.getCanonicalPath();
                canonicalPath = canonicalPath.substring(0, canonicalPath.length() - 6);
                // 获取 classpath 路径的长度
                int len = CLASS_PATH.getCanonicalPath().length();

                canonicalPath = canonicalPath.substring(len + 1);
                // 将 路径分隔符 替换成 .
                return canonicalPath.replace(File.separator, ".");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }).collect(Collectors.toList());

    }

    /**
     * 读取目录下的所有 .class 文件
     * @param directory
     * @return
     */
    private static List<File> readClassFile(File directory) {
        List<File> list = new ArrayList<>();
        if (!directory.exists() || directory.isFile()) {
            // 抛出一个异常
            throw new RuntimeException("directory 应该是一个目录、但传入的是一个可能不存在路径或是一个文件");
        }
        // 获取 该目录下所有的 资源
        File[] files = directory.listFiles(file -> (file.isDirectory() ||
                file.getName().endsWith(".class")) && !file.getName().equals("module-info.class"));

        // 判断 files 是否 不为 空
        if (files != null) {

            for (File file : files) {

                if (file.isFile()) {
                    list.add(file);
                } else {
                    List<File> files1 = readClassFile(file);
                    list.addAll(files1);
                }
            }
        }
        return list;
    }

    /**
     * 获取一个类 中所有的属性 (包含父类)
     * @param clz
     * @return
     */
    public static List<Field> getAllFields(Class<?> clz) {
        List<Field> list = new ArrayList<>();
        do {
            Field[] declaredFields = clz.getDeclaredFields();
            List<Field> fields = Arrays.asList(declaredFields);
            list.addAll(fields);

            clz = clz.getSuperclass();

        } while (clz != null);

        return list;
    }

    /**
     * 获取所有父类
     * @param clz
     * @return
     */
    public static List<Class<?>> getAllSuperClasses(Class<?> clz){
        List<Class<?>> list = new ArrayList<>();
        while(clz != Object.class) {
            clz = clz.getSuperclass() ;
            list.add(clz);
        }
        return list;
    }

    /**
     * 获取所有的父接口
     * @param clz
     * @return
     */
    public static List<Class<?>> getAllSuperInterfaces(Class<?> clz){
        List<Class<?>> list = new ArrayList<>();
        // 获取当前类的 所有的接口
        Class<?>[] interfaces = clz.getInterfaces();  // 如果找不到 , 返回 []

        for(Class<?> inter : interfaces) {
            if (!list.contains(inter))  list.add(inter);
            // 获取 Inter 的所有 父亲
            List<Class<?>> allSuperInterfaces = getAllSuperInterfaces(inter);
            list.addAll(allSuperInterfaces);
        }

        return list ;
    }

    /**
     * 获取所有的方法
     * @param clz
     * @return
     */
    public static List<Method> getAllMethods(Class<?> clz) {
        List<Method> list = new ArrayList<>();

        do {
            Method[] declaredMethods = clz.getDeclaredMethods();

            for(Method method : declaredMethods) {
                if (!list.contains(method)) list.add(method);
            }
            // 将 clz 指向 父类
            clz = clz.getSuperclass() ;
        }while (clz !=null) ;

        return list ;
    }

    /**
     * 判断一个注解是不是 @Component 或者 它的 子类
     * @param clazz
     * @return
     */
    public static boolean isComponent(Class<? extends Annotation> clazz) {
        // 获取 传入的注解 它的所有 父类
        List<Class<? extends Annotation>> allAnnotations = getAllAnnotations(clazz);
        // 判断传入的注解是否是 @Component 及其 子类
        return clazz == Component.class || allAnnotations.contains(Component.class);
    }

    /**
     * 获取 @Component 及其 子类 对应的类型 中的成员 和 值
     * @return
     */
    public static Map<String, String> getComponentMembers(Annotation annotation, final String defaultValue) {

        Class<? extends Annotation> aClass = annotation.annotationType();
        // 判断传入的注解是不是 Component
        if (!isComponent(aClass)){
            throw new RuntimeException("传入的注解" + aClass + "不是 @Component 或它的子类");
        }
        // 使用 反射 获取 该注解的成员
        Method[] declaredMethods = aClass.getDeclaredMethods();

        return Arrays.stream(declaredMethods).reduce(new HashMap<>(), (map, method) -> {
            // 获取 method 对应的方法名
            String key = method.getName() ;
            // 通过 反射调用 method 方法，获取 对应的值
            try {
                String invoke = (String) method.invoke(annotation);
                map.put(key, "".equals(invoke) ? defaultValue : invoke);
                return map ;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }, (a, b)-> a) ;
    }

    /**
     * 获取一个注解上的所有 注解
     * (获取该注解的所有父类)
     * @param clazz
     * @return
     */
    public static List<Class<? extends Annotation>> getAllAnnotations(Class<? extends Annotation> clazz) {
        List<Class<? extends Annotation>> list = new ArrayList<>();

        Annotation[] annotations = clazz.getAnnotations();
        // 遍历 annotations
        for(Annotation annotation : annotations) {
            Class<? extends Annotation> aClass = annotation.annotationType();
            if (!isMetaAnnotation(aClass)  && !list.contains(aClass)) {
                 list.add(aClass);
                 List<Class<? extends Annotation>> allAnnotations = getAllAnnotations(aClass);
                 list.addAll(allAnnotations);
            }
        }
        return list ;
    }

    /**
     * 判断一个注解是否是元注解
     * @param aClass
     * @return
     */
    public static boolean isMetaAnnotation(Class<? extends Annotation> aClass) {
        return META_ANNOTATIONS.contains(aClass);
    }
}
